package com.porm.framework.aop;

import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import com.porm.framework.annotation.Column;
import com.porm.framework.annotation.ORMOperate;
import com.porm.framework.annotation.Table;
import com.porm.framework.config.DataSourceConfig;
import com.porm.framework.enumeration.OperationType;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;

/**
 * AOP
 *
 * @author humdeef
 * @date 2018/11/13
 */
@Aspect
@Component
public class InterceptorAOP {

    @Autowired
    private DataSourceConfig dataSourceConfig;

    @Pointcut("execution( * com.porm.framework.mapper.Mapper.* (..))")
    private void allMethod() {
    }

    @Around(value = "allMethod()")
    public Object aroundController(ProceedingJoinPoint pjp) {
        Object arg = pjp.getArgs()[0];
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        Method method = null;
        List<String> parameterList = new ArrayList<>();
        List<Object> valueList = new ArrayList<>();
        try {
            method = target.getClass().getSuperclass().getMethod(msig.getName(), Object.class);
            ORMOperate annotation = method.getAnnotation(ORMOperate.class);
            Table table = arg.getClass().getDeclaredAnnotation(Table.class);
            String tableName = getTableName(arg, table);
            if (Objects.equals(annotation.type(), OperationType.SELECT)) {
                fieldValue(arg, parameterList, valueList);
                return runSql(tableName, parameterList, valueList, OperationType.SELECT);
            }
            if (Objects.equals(annotation.type(), OperationType.SAVE)) {
                fieldValue(arg, parameterList, valueList);
                return runSql(tableName, parameterList, valueList, OperationType.SAVE);
            } else if (Objects.equals(annotation.type(), OperationType.UPDATE)) {
                fieldValue(arg, parameterList, valueList);
                return runSql(tableName, parameterList, valueList, OperationType.UPDATE);
            } else if (Objects.equals(annotation.type(), OperationType.DELETE)) {
                fieldValue(arg, parameterList, valueList);
                return runSql(tableName, parameterList, valueList, OperationType.DELETE);
            }

        } catch (MySQLIntegrityConstraintViolationException e) { //Duplicate entry for key 'PRIMARY'
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Object runSql(String tableName, List<String> parameterList, List<Object> valueList, OperationType type) throws Exception {
        String sql = "";
        if (parameterList.size() > 0 && valueList.size() > 0) {
            sql = initSql(tableName, parameterList, valueList, type);
        }
        return executeSql(parameterList, sql, type);
    }


    private void fieldValue(Object arg, List<String> parameterList, List<Object> valueList) throws IllegalAccessException {
        Field[] declaredFields = arg.getClass().getDeclaredFields();
        for (int i = 0, size = declaredFields.length; i < size; i++) {
            declaredFields[i].setAccessible(true);
            String name = declaredFields[i].getName();
            Column column = declaredFields[i].getDeclaredAnnotation(Column.class);
            if (column == null) {
                parameterList.add(name);
            } else {
                parameterList.add(column.name());
            }
            if (declaredFields[i].getType() == Date.class) {
                Date o = (Date) declaredFields[i].get(arg);
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                valueList.add(simpleDateFormat.format(o));
            } else {
                valueList.add(declaredFields[i].get(arg));
            }
        }
    }


    private String getTableName(Object arg, Table table) {
        String tableName = arg.getClass().getName();
        if (table != null) {
            tableName = table.name();
        }
        return tableName;
    }

    private Object executeSql(List<String> parameterList, String sql, OperationType type) throws Exception {
        if (Objects.isNull(sql)) {
            return 0;
        }
        Connection conn = null;
        Statement stmt = null;
        try {
            // 注册 JDBC 驱动
            Class.forName(dataSourceConfig.getDriverClassName());

            // 打开链接
            System.out.println("连接数据库...");
            conn = DriverManager.getConnection(dataSourceConfig.getUrl(), dataSourceConfig.getUsername(), dataSourceConfig.getPassword());

            // 执行查询
            System.out.println(" 实例化Statement对象...");
            stmt = conn.createStatement();
            if (!Objects.equals(type, OperationType.SELECT)) {
                Boolean result = stmt.execute(sql);
                if (result) {
                    return 1;
                }
            } else {
                ResultSet rs = stmt.executeQuery(sql);
                List<Map<String, Object>> list = new ArrayList<>();
                // 展开结果集数据库
                while (rs.next()) {
                    Map<String, Object> map = new HashMap<>(parameterList.size());
                    for (int i = 0; i < parameterList.size(); i++) {
                        map.put(parameterList.get(i), rs.getObject(parameterList.get(i)));
                    }
                    list.add(map);
                }
                rs.close();
                return list;

            }
            // 完成后关闭
            stmt.close();
            conn.close();
        } catch (SQLException se) {
            // 处理 JDBC 错误
            se.printStackTrace();
            throw se;
        } catch (Exception e) {
            // 处理 Class.forName 错误
            e.printStackTrace();
            throw e;
        } finally {
            // 关闭资源
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException se2) {
            }// 什么都不做
            try {
                if (conn != null) conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
        }
        return 0;
    }

    private String initSql(String
                                   tableName, List<String> parameterList, List<Object> valueList, OperationType type) {
        if (Objects.equals(OperationType.SAVE, type)) {
            StringBuilder builder = new StringBuilder("insert into ");
            builder.append(tableName).append(" (");
            String join = String.join(",", parameterList);
            builder.append(join).append(") values (");
            for (Object o : valueList) {
                if (o.getClass() == String.class) {
                    builder.append("'").append(o).append("'");
                } else {
                    builder.append(o);
                }
                builder.append(",");
            }
            builder.replace(builder.length() - 1, builder.length(), "");

            builder.append(");");
            return builder.toString();
        } else if (Objects.equals(OperationType.UPDATE, type)) {
            StringBuilder builder = new StringBuilder("update ");
            builder.append(tableName).append(" set ");

            for (int i = 0; i < valueList.size(); i++) {
                if (valueList.get(i) != null) {

                    builder.append(parameterList.get(i)).append(" = ");
                    if (valueList.get(i).getClass() == String.class) {
                        builder.append("'").append(valueList.get(i)).append("'");
                    } else {
                        builder.append(valueList.get(i));
                    }
                    builder.append(",");
                }
            }
            builder.replace(builder.length() - 1, builder.length(), "");
            builder.append(" where id = ").append(valueList.get(0));
            return builder.toString();
        } else if (Objects.equals(OperationType.DELETE, type)) {
            StringBuilder builder = new StringBuilder("delete from  ");
            builder.append(tableName).append(" where ");
            builder.append(parameterList.get(0))
                    .append(" = ")
                    .append(valueList.get(0))
                    .append(";");
            return builder.toString();
        } else if (Objects.equals(OperationType.SELECT, type)) {
            StringBuilder builder = new StringBuilder("select * from  ");
            builder.append(tableName)
                    .append(";");
            return builder.toString();
        }
        return null;
    }
}
